//#include "tscell.h"
#include "tsdna.h"
#include "tssynapse.h"
#include <stdlib.h>
#include <glib-object.h>

typedef struct _TsCell {
  GObject parent_instance;

  gint64 id;  //space
  gint64 value;
  //gint64 chec;
  gint64 threshold;

  gboolean status;

  gchar *name;

  gpointer dendrite;//树突 input 250
  gpointer axon;//轴突 output 250
//  synapse;

  gpointer dna;

  gpointer son;
  GObject *next;
  /*
  gint type;
  GList* eptors;   // 该神经元上的受体，一个GList结构体对象，其中的数据类型为int（要求所有神经元共用一个受体字典）
  gboolean activated;
  gint threshold;
  gint current;
  gint activated_time;
  GPtrArray *genome;  // 基因组，即指向 Gene 对象的指针数组
  GList *activators;  // 活性化中心中存储的绑定到转录因子的基因列表
  GObject nucleus;//核
  GObject membrane;//膜
  GObject centrosome;//中心体
  */
} TsCell;
typedef struct _TsCellClass {
    GObjectClass parent_class;
    void (*value_changed) (TsCell *self, int value);
} TsCellClass;
enum {
  PROP_0,
  PROP_ID, PROP_VALUE, PROP_THRESHOLD,
  PROP_STATUS,
  PROP_NAME,
  PROP_DENDRITE, PROP_AXON,
  PROP_DNA,
  PROP_SON, PROP_NEXT,
  N_PROPERTIES };

static GParamSpec *properties[N_PROPERTIES] = { NULL, };

//TsCell *ts_cell_new (gpointer dna, gint64 id);
G_DEFINE_TYPE (TsCell, ts_cell, G_TYPE_OBJECT)

static void
ts_cell_set_id (TsCell *obj,
                     gint64        id)
{
  if (obj->id != id)
    {
      obj->id = id;

      g_assert (properties[PROP_ID] != NULL);
      g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_ID]);
    }
}
static void
ts_cell_set_value (TsCell *obj,
                     gint64        value)
{
  if (obj->value != value)
    {
      obj->value = value;

      g_assert (properties[PROP_VALUE] != NULL);
      g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_VALUE]);
    }
}
static void
ts_cell_set_threshold (TsCell *obj,
                     gint64        value)
{
  if (obj->threshold != value)
    {
      obj->threshold = value;

      g_assert (properties[PROP_THRESHOLD] != NULL);
      g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_THRESHOLD]);
    }
}
static void
ts_cell_set_status (TsCell *obj,
                     gboolean    status)
{
  status = !!status;

  if (obj->status != status)
    {
      obj->status = status;

      g_assert (properties[PROP_STATUS] != NULL);
      g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_STATUS]);
    }
}

static void
ts_cell_set_name (TsCell  *obj,
                     const gchar *name)
{
  if (g_strcmp0 (obj->name, name) != 0)
    {
      g_free (obj->name);
      obj->name = g_strdup (name);

      g_assert (properties[PROP_NAME] != NULL);
      g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_NAME]);
    }
}

static void
ts_cell_set_dendrite (TsCell  *obj,
                     gpointer value)
{
  obj->dendrite = value;
}
static void
ts_cell_set_axon (TsCell  *obj,
                     gpointer value)
{
  obj->axon = value;
}
static void
ts_cell_set_dna (TsCell  *obj,
                     gpointer value)
{
  obj->dna = value;
}
static void
ts_cell_set_son (TsCell  *obj,
                     gpointer value)
{
  obj->son = value;
}
static void
ts_cell_set_next (TsCell  *obj,
                     gpointer value)
{
  obj->next = (GObject *) value;
}

static void
ts_cell_finalize (GObject *gobject)
{
  TsCell *self = (TsCell *) gobject;

  g_free (self->name);

  g_object_notify (gobject, "port");
  g_object_notify_by_pspec (gobject, properties[PROP_STATUS]);

  G_OBJECT_CLASS (ts_cell_parent_class)->finalize (gobject);
}

static void
ts_cell_set_property (GObject *gobject,
                          guint prop_id,
                          const GValue *value,
                          GParamSpec *pspec)
{
  TsCell *tobj = (TsCell *) gobject;

  g_assert_cmpint (prop_id, !=, 0);
  g_assert_cmpint (prop_id, !=, N_PROPERTIES);
  g_assert (pspec == properties[prop_id]);

  switch (prop_id)
    {
    case PROP_ID:
      ts_cell_set_id (tobj, g_value_get_int64 (value));
      break;
    case PROP_VALUE:
      //ts_cell_set_value (tobj, g_value_get_int64 (value));
      g_signal_emit_by_name (tobj, "value-changed", tobj->value);
      break;
    case PROP_THRESHOLD:
      ts_cell_set_threshold (tobj, g_value_get_int64 (value));
      break;

    case PROP_STATUS:
      ts_cell_set_status (tobj, g_value_get_boolean (value));
      break;

    case PROP_NAME:
      ts_cell_set_name (tobj, g_value_get_string (value));
      break;
    case PROP_DENDRITE:
      ts_cell_set_dendrite (tobj, g_value_get_pointer (value));
      g_signal_emit_by_name (tobj, "dendrite-changed", tobj->value);
      break;
    case PROP_AXON:
      ts_cell_set_axon (tobj, g_value_get_pointer (value));
      break;
    case PROP_DNA:
      ts_cell_set_dna (tobj, g_value_get_pointer (value));
      break;
    case PROP_SON:
      ts_cell_set_son (tobj, g_value_get_pointer (value));
      break;
    case PROP_NEXT:
      ts_cell_set_next (tobj, g_value_get_object (value));
      break;

    default:
      g_assert_not_reached ();
    }
}

static void
ts_cell_get_property (GObject *gobject,
                          guint prop_id,
                          GValue *value,
                          GParamSpec *pspec)
{
  TsCell *tobj = (TsCell *) gobject;

  g_assert_cmpint (prop_id, !=, 0);
  g_assert_cmpint (prop_id, !=, N_PROPERTIES);
  g_assert (pspec == properties[prop_id]);

  switch (prop_id)
    {
    case PROP_ID:
      g_value_set_int64 (value, tobj->id);
      break;
    case PROP_VALUE:
      g_value_set_int64 (value, tobj->value);
      break;
    case PROP_THRESHOLD:
      g_value_set_int64 (value, tobj->threshold);
      break;

    case PROP_STATUS:
      g_value_set_boolean (value, tobj->status);
      break;

    case PROP_NAME:
      g_value_set_string (value, tobj->name);
      break;

    case PROP_DENDRITE:
      g_value_set_pointer (value, tobj->dendrite);
      break;
    case PROP_AXON:
      g_value_set_pointer (value, tobj->axon);
      break;
    case PROP_DNA:
      g_value_set_pointer (value, tobj->dna);
      break;

    case PROP_NEXT:
      g_value_set_object (value, (gpointer) tobj->next);
      break;
    case PROP_SON:
      g_value_set_pointer (value, tobj->son);
      break;

    default:
      g_assert_not_reached ();
    }
}

static void
ts_cell_class_init (TsCellClass *klass)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);

  properties[PROP_ID] = g_param_spec_int64 ("id", "id", "id",
      -1, G_MAXINT64, 0, G_PARAM_READWRITE);
  properties[PROP_VALUE] = g_param_spec_int64 ("value", "value", "value",
      -1, G_MAXINT64, 0, G_PARAM_READWRITE);
  properties[PROP_THRESHOLD] = g_param_spec_int64 (
      "threshold", "threshold", "threshold",
      -1, G_MAXINT64, 0, G_PARAM_READWRITE);

  g_signal_new("dendrite-changed", G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_FIRST,
      G_STRUCT_OFFSET(TsCellClass, value_changed), NULL, NULL, NULL,
      G_TYPE_NONE, 1, G_TYPE_INT64);
  properties[PROP_STATUS] = g_param_spec_int ("status", "Status", "Status",
      -1, G_MAXINT, 0, G_PARAM_READWRITE);
  properties[PROP_NAME] = g_param_spec_string ("name", "Name", "Name",
      NULL, G_PARAM_READWRITE);
  properties[PROP_DENDRITE] = g_param_spec_pointer (
      "dendrite", "dendrite", "dendrite",
      G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
  properties[PROP_AXON] = g_param_spec_pointer ("axon", "axon", "axon",
      G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
  properties[PROP_DNA] = g_param_spec_pointer ("dna", "dna", "dna",
      G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
  properties[PROP_SON] = g_param_spec_object ("son", "son", "son",
      G_TYPE_OBJECT, G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
  properties[PROP_NEXT] = g_param_spec_object ("next", "next", "next",
      G_TYPE_OBJECT, G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
  g_signal_new("value-changed", G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_FIRST,
      G_STRUCT_OFFSET(TsCellClass, value_changed), NULL, NULL, NULL,
      G_TYPE_NONE, 1, G_TYPE_INT64);

  gobject_class->set_property = ts_cell_set_property;
  gobject_class->get_property = ts_cell_get_property;
  gobject_class->finalize = ts_cell_finalize;

  g_object_class_install_properties (gobject_class, N_PROPERTIES, properties);
}

static void value_changed_handler(TsCell *self, gint64 value)
{
  g_usleep (1000);
  //id 1:in 0:out
  g_print("L%d f-%s cell[%ld] threshold[%"G_GINT64_FORMAT"] value changed to %ld\n",
      __LINE__, __func__, self->id, self->threshold, value);
  // update time
  gint64 tt, cell_value = 0, d_value, t1, proportion;
  TsSynapse *synapse, *next;

  tt = g_get_real_time ();
//  g_object_set (self, "time", tt, NULL);
  synapse = self->dendrite;
  while (synapse) {
    g_object_get (synapse, "next", &next, "value", &d_value, "time", &t1, 
        "proportion", &proportion, NULL);
    if (tt-t1 < 5000000) {
      cell_value += d_value*proportion;
    }
    synapse = next;
  }
  if (cell_value > self->threshold) {
    if (self->axon)
      g_signal_emit_by_name (self->axon, "value-changed", cell_value);
  }
}
static void
ts_cell_init (TsCell *self)
{
  g_signal_connect (self, "value-changed", G_CALLBACK(value_changed_handler),
      self);
}
static void
ts_cell_synapse_create (TsCell *self)
{
  TsDna *dna = NULL;
  TsSynapse *dendrite, *axon, *now, *next;

  dna = ts_dnas_get_object (self->dna, "id", &(self->id));
  g_object_get (dna, "dendrite", &dendrite, "axon", &axon, NULL);
  self->dendrite = dendrite;
  self->axon = axon;
  now = dendrite;
  while (now) {
    g_object_get (now, "next", &next, NULL);
    g_object_set (now, "parent", self, NULL);
    now = next;
  }
  now = axon;
  while (now) {
    g_object_get (now, "next", &next, NULL);
    g_object_set (now, "parent", self, NULL);
    now = next;
  }
}
void
ts_cell_synapse_link (TsCell *self)
{
  TsDna *dna = NULL, *next_dna;
  TsSynapse *dendrite, *axon, *next_dendrite, *next_axon;
  GList *l = NULL;
  gint64 dendrite_id, id;

  axon = self->axon;
  while (axon) {
    g_object_get (axon, "next", &next_axon, "id", &id,
        "dendrite_id", &dendrite_id, NULL);
    g_debug ("axon[%"G_GINT64_FORMAT"] link dendrite[%"G_GINT64_FORMAT"]",
        id, dendrite_id);
    dna = self->dna;
    while (dna) {
      g_object_get (dna, "next", &next_dna, "dendrite", &dendrite, NULL);
      while (dendrite) {
        g_object_get (dendrite, "next", &next_dendrite, "id", &id, NULL);
        if (id == dendrite_id) {
          l = g_list_append (l, dendrite);
        }
        dendrite = next_dendrite;
      }
      dna = next_dna;
    }
    g_object_set (axon, "peer_list", l, NULL);
    axon = next_axon;
  }

}

//TsCell *ts_cell_new (void)
TsCell *ts_cell_new (gpointer dna, gint64 id, gint64 threshold)
{
  TsCell *cell;
  cell =  g_object_new (ts_cell_get_type (), "dna", dna, "id", id,
      "threshold", threshold, NULL);
  g_debug ("L%d f-%s new cell id:%ld", __LINE__, __func__, id);
  ts_cell_synapse_create (cell);
  //ts_cell_run (cell);

  return cell;
  //return g_object_new (ts_cell_get_type (), "dna", dna, "value", 22, NULL);
}
TsCell *ts_cells_get_object (TsCell *gobject,
                          const gchar *name,
                          gpointer value)
{
  TsCell *tobj = gobject;
  guint prop_id;
  gboolean found = FALSE;
  if (!g_strcmp0 (name, "id")) {
    prop_id = PROP_ID;
  }
  while (tobj) {
  //g_debug ("%s\tid:%d\n", __func__, tobj->id);
    switch (prop_id)
      {
      case PROP_ID:
        if (tobj->id == *(gint64 *)value) {
          found = TRUE;
        }
        break;
      default:
        g_assert_not_reached ();
      }
    if (found)
      break;
    tobj = (TsCell *) tobj->next;
  }
  return tobj;
}
